using System.Linq.Expressions;
using Common.Page;
using Domain.Repositories;
using Microsoft.EntityFrameworkCore;

namespace Infrastructure.Repositories;

public abstract class BaseRepository<TEntity>(UserDbContext dbContext) : IBaseRepository<TEntity>
    where TEntity : class, new()
{
    public virtual async Task<TEntity?> AddAsync(TEntity? entities)
    {
        if (entities == null) return null;

        await dbContext.Set<TEntity>().AddAsync(entities);

        return entities;
    }

    public virtual async Task AddRangeAsync(List<TEntity> entities)
    {
        var list = entities.ToList();

        await dbContext.Set<TEntity>().AddRangeAsync(list);
    }

    public virtual async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> where, bool isIncludeDelete = false)
    {
        var query = dbContext.Set<TEntity>().AsNoTracking().Where(where);
        if (isIncludeDelete) query = query.IgnoreQueryFilters();

        return await query.AnyAsync();
    }

    public virtual async Task<int> CountAsync(Expression<Func<TEntity, bool>> where, bool isIncludeDelete = false)
    {
        var query = dbContext.Set<TEntity>().AsNoTracking().Where(where);

        if (isIncludeDelete) query = query.IgnoreQueryFilters();
        return await query.CountAsync();
    }

    public virtual void DeleteRange(List<TEntity> entities)
    {
        dbContext.Set<TEntity>().RemoveRange(entities);
    }

    public virtual void Delete(TEntity entity)
    {
        dbContext.Set<TEntity>().Remove(entity);
    }

    public virtual async Task<TEntity?> GetAsync(Expression<Func<TEntity, bool>> where, bool isIncludeDelete = false)
    {
        var query = dbContext.Set<TEntity>().AsNoTracking().Where(where);

        if (isIncludeDelete) query = query.IgnoreQueryFilters();
        return await query.FirstOrDefaultAsync();
    }

    public virtual async Task<List<TEntity>> GetListAsync<TKey>(Expression<Func<TEntity, bool>> where,
        Expression<Func<TEntity, TKey>> orderBy, bool isDesc = true, bool isIncludeDelete = false)
    {
        var query = dbContext.Set<TEntity>().AsNoTracking().Where(where);

        if (isIncludeDelete) query = query.IgnoreQueryFilters();

        query = isDesc ? query.OrderByDescending(orderBy) : query.OrderBy(orderBy);

        return await query.ToListAsync();
    }

    public virtual async Task<List<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> where,
        bool isIncludeDelete = false)
    {
        var query = dbContext.Set<TEntity>().AsNoTracking().Where(where);

        if (isIncludeDelete) query = query.IgnoreQueryFilters();

        return await query.ToListAsync();
    }

    public virtual async Task<PageList<TEntity>> GetPageListAsync<TKey>(Expression<Func<TEntity, bool>> where,
        Expression<Func<TEntity, TKey>> orderBy, int pageIndex, int pageSize, bool isDesc = true,
        bool isIncludeDelete = false)
    {
        var query = dbContext.Set<TEntity>().AsNoTracking().Where(where);

        if (isIncludeDelete) query = query.IgnoreQueryFilters();

        var count = await query.CountAsync();

        query = isDesc ? query.OrderByDescending(orderBy) : query.OrderBy(orderBy);

        var list = await query.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();

        return new PageList<TEntity>(list, pageIndex, pageSize, count);
    }

    public virtual TEntity? Update(TEntity? entities)
    {
        if (entities == null) return null;

        dbContext.Set<TEntity>().Update(entities);

        return entities;
    }

    public virtual List<TEntity>? UpdateRange(List<TEntity>? entities)
    {
        if (entities == null) return null;

        dbContext.Set<TEntity>().UpdateRange(entities);

        return entities;
    }
}